#!/bin/bash
#
# #####################################################
# QsYouTubeTrailerScraper - QYTTS - v0.25
# #####################################################
#
# This script will scrape your Kodi library for movie titles, then search
# YouTube for it's trailer. It'll download the trailers to a specified
# directory, which is used by the Cinema Experience script for Kodi to
# load trailers. This way you can see the trailers for films you own,
# withought having to dea with all the buffering that comes from streaming.
#
# I know there are other tools out there to get trailers for your library,
# but most were windows based. The ones that weren't would just grab trailers
# for any film, whether you have it or not.
#
# This was designed for my original Raspberry Pi Model B, running OSMC.
# As such, it'll probably run on any Linux Kodi setup.
#
#
# Please see the included README.md file for more information
#
#
#
# Copyright © 2015 Category <categoryNOSPAM@quintendo.uk>
# This work is free. You can redistribute it and/or modify it under the
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See the COPYING file for more details.



#################
#               #
# CONFIGURATION #
#               #
#################

###
# REQUIRED
###

# Update the below before using, if this default doesn't match your storage plans

ts_trailerdir="$HOME/Trailers/"
# Location to save trailers, with trailing slash


###
# OPTIONAL
###

ts_databasedir="$HOME/.kodi/userdata/Database/"
# Location of your Kodi database directory, normally "$HOME/.kodi/userdata/Database/"

ts_tempdir="/tmp/"
# Location to store temporary woking files, with trailing slash

ts_maxsize=52428800
# Maximum filesize of Trailers in bytes (to prevent full films being saved as trailers)
# Default 50MB is 52428800

ts_fix_dependencies=false
# Option to fix dependencies if they are missing - script may ask for sudo password,
# and install files to /usr/bin, or via apt-get
# Default is false, for safety



  #############
###           ###
# # FUNCTIONS # #
###           ###
  #############


function trailer_dl {
    # Clear temp files/variable
    cat /dev/null > $ts_tempdir"YTSearchList"
    cat /dev/null > $ts_tempdir"TopResult"
    cat /dev/null > $ts_tempdir"YTDLErrors"
    YTCode=""
    
    # Prepare search target
    # Convert passed film name to the format "Film+Name+trailer", to pipe into the YouTube search URL
    YTTarget=${1// /+}+"trailer"
    # Prepare new filename
    YTNewName=$1" [Trailer].mp4"
    
    # Check if local trailer already exists
    # -e returns positive if file of the new name already exists
    if [ -e $ts_trailerdir"$YTNewName" ]
    then
	# File exists
	echo Trailer for $1 already exists, skipping download
	return
    else
	# No file found
	echo Searching YouTube for $1 trailer
	
	# Scrape top result from YouTube search
	# The process is as follows:-
	# 1. curl downloads html of the results page, after searching for the "Film+Name+trailer"
	# 2. grep returns any section containing 'videoId":' and the following code. -o to return just the search term, and not the entire line as the current YouTube results page is quite obfuscated and very few lines, all exceedingly long
	# 3. sed strips away the cruft, leaving just the YouTube videoId string
	# 4. List of all codes is piped to the "YTSearchList" temp file
	# 5. head saves the first result to "TopResult" temp file - the first videoId in a YouTube results page is always the top search result
	curl -s https://www.youtube.com/results\?search_query\=$YTTarget | grep -o videoId\"\:............ | sed 's/videoId\"\:\"//' > $ts_tempdir"YTSearchList"
	head -n 1 $ts_tempdir"YTSearchList" > $ts_tempdir"TopResult"
	
	# Extract YouTube ID from TopResult
	YTCode=$(cat $ts_tempdir"TopResult")
	echo YouTube ID: $YTCode
		
	# Check if first result is a trailer
	# youtube-dl with -e returns the title of the video store as YTTitle
	YTTitle=$(youtube-dl -e "https://www.youtube.com/watch?v=$YTCode" 2> $ts_tempdir"YTDLErrors")

	# Skip film if error encountered extracting title
	if [ $(cat $ts_tempdir"YTDLErrors" | grep ERROR | wc -l) -gt 0 ]
	then
	    echo youtube-dl encountered an error, skipping film
	    LogFailure "[youtube-dl error]   |"
	    return
	fi
	
	# If the title doesn't include the word "trailer" it is disregarded
	if [ $(echo $YTTitle | grep -i trailer | wc -l) = 1 ]
	then
	    echo First result is a trailer, proceeding
	    echo Downloading trailer for $1

	    # Breakdown of the download request:-
	    # -q      - Quiet to suppress
	    # --id    - Use the videoId as filename
	    # -f best - Save highest quality as .mp4 file
	    # Uses the full url in quotes, to hopefully avoid issues where videoId includes punctuation
	    # 2> to pipe all error output into "YTDLErrors" temp file
	    youtube-dl -q --id -f best "https://www.youtube.com/watch?v=$YTCode" 2> $ts_tempdir"YTDLErrors"
		    
	    # Check to see if any of the error log contains the "ERROR" keyword - if so consider it a failure and move on
	    if [ $(cat $ts_tempdir"YTDLErrors" | grep ERROR | wc -l) -gt 0 ]
	    then
		echo youtube-dl encountered an error, skipping film
		LogFailure "[youtube-dl error]   |"
		return
	    else
		# Store downloaded filename based on videoId
		YTFileName=$YTCode.mp4
		    
		# Store filesize of trailer
		YTFileSize=$(wc -c <"$YTFileName")
		    
		# Check filesize against user-set max size
		# If too large, delete
		# If not, move to the trailer directory in the form "Film Name [Trailer].mp4"
		if [ $YTFileSize -gt $ts_maxsize ]
		then
		    echo File too large, removing
		    rm "./$YTFileName"
		    LogFailure "[filesize too large] |"
		    return
		else
		    mv "./$YTFileName" $ts_trailerdir"$YTNewName"
		    echo Saved as $YTNewName
		fi
	    fi
	else
	    echo Top YouTube result is not a trailer, skipping download
	    LogFailure "[No trailer found]   |"
	    return
	fi	
    fi	
}


function LogFailure {
    # Count how many downloads failed
    let FailedDL=$FailedDL+1

    # Store failure reason & film name to temp file
    echo "$1 $ScanFilm" >> $ts_tempdir"FailedFilms"
}



#      ################      #
 #   ###              ###   #
###### # SCRIPT START # ######
 #   ###              ###   #
#      ################      #
echo QsYouTubeTrailerScraper - QYTTS
echo v0.25- 2020-11-15
echo



#########################
#                       #
# Test for dependencies #
#                       #
#########################
if [ $(which youtube-dl) ]
then
    echo youtube-dl found
    if [ $(which sqlite3) ]
    then
	echo SQLite3 found
	echo Dependencies met, continuing...
    else
	if [ "$ts_fix_dependencies" = true ]
	then
	    echo Attempting to install SQLite3
	    echo sudo password may be requested
	    sudo apt-get update -y
	    sudo apt-get install sqlite3 -y
	    if [ $? = 0 ]
	    then
		echo Successfully installed SQLite3
	    else
		echo Something has gone wrong, please install SQLite3 manually \(see README\)
		exit
	    fi
	else
	    echo SQLite3 not installed, please see README
	    exit
	fi
    fi
else
    if [ "$ts_fix_dependencies" = true ]
    then
	echo Attempting to install youtube-dl
	echo sudo password may be requested
	sudo curl https://yt-dl.org/latest/youtube-dl -o /usr/local/bin/youtube-dl ; sudo chmod a+rx /usr/local/bin/youtube-dl
	if [ $? = 0 ]
	then
	    echo Successfully installed youtube-dl
	else
	    echo Something has gone wrong, please install youtube-dl manually \(see README\)
	    exit
	fi
    else
	echo youtube-dl not installed, please see README
	exit
    fi
fi



##############
#            #
# Initialize #
#            #
##############

echo Initializing...

# Create clean temp files
cat /dev/null > $ts_tempdir"CurrentMovie"
cat /dev/null > $ts_tempdir"MovieList"
cat /dev/null > $ts_tempdir"YTSearchList"
cat /dev/null > $ts_tempdir"TopResult"
cat /dev/null > $ts_tempdir"FailedFilms"
cat /dev/null > $ts_tempdir"YTDLErrors"
FailedDL=0

# Log start directory, then move to temp folder ready to download
StartDir=$(pwd)
cd $ts_tempdir

# Create trailer directory if it doesn't exist
if [ ! -e $ts_trailerdir ]
then
    echo "Trailer directory doesn't exist, creating it now"
    mkdir $ts_trailerdir
fi




########################
#                      #
# Select Database File # 
#                      #
########################
echo Finding curent Kodi database

# All Kodi video databases come in the format "MyVideosXXX.db", where the XXX is any seemingly
# random number that increases as Kodi is updated. To find the latest, this line performs:-
# 1. List all files in databse directory
# 2. Limit to "MyVideos" files
# 3. Show only the trailing numbers from these files
# 4. Sort in decreasing numerical value
# 5. Store the highest number as DBNumber, to be the base for the variable DBFile
DBNumber=$(ls $ts_databasedir | grep MyVideos | grep -o '[0-9]\{1,\}' | sort -gr | head -n 1)
DBFile=MyVideos"$DBNumber".db
echo Using database file $DBFile



#####################
#                   #
# Extract MovieList #
#                   #
#####################
echo Extracting MovieList from Kodi database
sqlite3 $ts_databasedir$DBFile "select c00 from movie order by c00;" >> $ts_tempdir"MovieList"

MovieCount=$(wc -l < $ts_tempdir"MovieList")
echo $MovieCount movies found



  #############
###           ###
# # MAIN LOOP # #
###           ###
  #############

# Loop through the Movie list
COUNTER=0
while [ $COUNTER -lt $MovieCount ]; do
	# Cut a single film out of the list based on the Counter
	let COUNTER=$COUNTER+1
	head -n$COUNTER $ts_tempdir"MovieList" > $ts_tempdir"CurrentMovie"
	ScanFilm=$(tail -n1 $ts_tempdir"CurrentMovie")
	
	
	# Pass movie name to the download handler
	trailer_dl "$ScanFilm"
done

echo All Movies checked.



###################
#                 #
# Report failures #
#                 #
###################

# Print list of failed films, so user can find get them manually
if [ $FailedDL -gt 0 ]
then
    echo Failed to find trailers for the following $FailedDL movies...
    echo -------------
    cat $ts_tempdir"FailedFilms"
    echo -------------

    # Save a copy of all failed films in a log file
    CurrDate=$(date)
    echo "QYTTS failure log for "$CurrDate > $StartDir"/FailedTrailers.log"
    cat $ts_tempdir"FailedFilms" >> $StartDir"/FailedTrailers.log"
    echo Log of failures saved as "FailedTrailers.log"
else
    echo All missing trailers downloaded successfully
fi


############
#          #
# CLEANING #
#          #
############

echo Cleaning up...

# Remove temp files
rm $ts_tempdir"CurrentMovie"
rm $ts_tempdir"MovieList"
rm $ts_tempdir"YTSearchList"
rm $ts_tempdir"TopResult"
rm $ts_tempdir"FailedFilms"
rm $ts_tempdir"YTDLErrors"
    
    
# Check for mp4 files in temp directory and remove if found
# (sometimes left behind from youtube-dl issues)
if [ $(ls $ts_tempdir | grep .mp4 | wc -l) -gt 0 ]
then
    rm $ts_tempdir*.mp4
fi
    
# Return to initial directory
cd $StartDir



########
# Exit #
########

echo Thanks for using QYTTS
exit
